﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Timers;

namespace WebRamosUC.Models
{
    public class RepositorioRecomendacion : WebRamosUC.Models.IRepositorioRecomendacion
    {
        private WebRamosUCDataContext db;

        private static bool revisandoReportes = false;
        private static Timer clock;

        public RepositorioRecomendacion(WebRamosUCDataContext dataContext)
        {
             db = dataContext;

        }
        public RepositorioRecomendacion()
        {
            this.db = new WebRamosUCDataContext();
        }


        /// <summary>
        /// Retorna todas las recomendaciones hechas a un profesor
        /// </summary>
        public IQueryable<Recomendacion> GetRecomendaciones(Profesor profesor)
        {
            return from recomendacion in db.Recomendacions
                   where recomendacion.Seccion.ProfesorSecciones.Any(ps => ps.Profesor == profesor) //recomendacion.Seccion.GetProfesores().Contains(profesor)
                   select recomendacion;
        }

        /// <summary>
        /// Retorna todas las recomendaciones hechas a un ayudante
        /// </summary>
        public IQueryable<Recomendacion> GetRecomendaciones(Ayudante ayudante)
        {
            return from recomendacion in db.Recomendacions
                   where recomendacion.Seccion.GetAyudantes().Contains(ayudante)
                   select recomendacion;
        }

        /// <summary>
        /// Retorna todas las recomendaciones hechas a un curso
        /// </summary>
        public IQueryable<Recomendacion> GetRecomendaciones(Curso curso)
        {
            return from recomendacion in db.Recomendacions
                   where recomendacion.Seccion.Curso.Sigla == curso.Sigla
                   select recomendacion;
        }

        /// <summary>
        /// Retorna todas las recomendaciones con calificacion igual o superior a la entregada.
        /// </summary>
        public IQueryable<Recomendacion> GetRecomendaciones(int calificacion)
        {
            return from recomendacion in db.Recomendacions
                   where recomendacion.Calificaciones.Sum(p => p.Valor) >= calificacion  //Una calificacion puede tener valor +1 o -1
                   orderby recomendacion.Calificaciones.Sum(p => p.Valor)
                   select recomendacion;
        }

        #region Ranking

        /// <summary>
        /// Retorna las ultimas recomendaciones insertadas.
        /// </summary>
        public IQueryable<Recomendacion> GetUltimasRecomendaciones(int cantidad)
        {
            var recs = from recomendacion in db.Recomendacions
                       where recomendacion.Activo
                       orderby recomendacion.Fecha descending
                       select recomendacion;
            return recs.Take(cantidad);
        }

        /// <summary>
        /// Retorna las ultimas recomendaciones insertadas en una facultad
        /// </summary>
        public IQueryable<Recomendacion> GetUltimasRecomendaciones(int cantidad, int idFacultad)
        {
            var recs = from recomendacion in db.Recomendacions
                       where recomendacion.Activo && recomendacion.Seccion.Curso.IDFacultad == idFacultad
                       orderby recomendacion.Fecha descending
                       select recomendacion;

            return recs.Take(cantidad);
        }

        #endregion

        public IQueryable<Reporte> GetReportesSinRevision()
        {
            var reportes = from reporte in db.Reportes
                           where reporte.Revisado == 0
                           orderby reporte.Fecha ascending
                           select reporte;

            return reportes;
        }

        public Recomendacion GetRecomendacion(int id_recomendacion)
        {
            if (id_recomendacion < 0)
                return null;

            return db.Recomendacions.SingleOrDefault(d => d.IDRecomendacion == id_recomendacion);

        }

        /// <summary>
        /// Agrega una recomendacion a la base de datos.
        /// </summary>
        public bool AddRecomendacion(Recomendacion recomendacion)
        {
            db.Recomendacions.InsertOnSubmit(recomendacion);
            Save();

            return true;
        }

        /// <summary>
        /// Agrega una recomendacion a la base de datos.
        /// </summary>
        public bool AddCalificacion(Calificacion calificacion)
        {
            db.Calificacions.InsertOnSubmit(calificacion);
            Save();

            return true;
        }

        /// <summary>
        /// Retorna todas las recomendaciones hechas por un usuario
        /// </summary>
        /// <param name="usuario"></param>
        /// <returns></returns>
        public IQueryable<Recomendacion> GetRecomendaciones(string usuarioName)
        {
            
            return from recomendacion in db.Recomendacions
                   where recomendacion.Usuario.Nombre == usuarioName
                   select recomendacion;
        }
        /// <summary>
        /// Agrega un reporte a la base de datos y envia el mail al admin
        /// </summary>
        public bool GuardaReporte(Reporte reporte)
        {
            db.Reportes.InsertOnSubmit(reporte);
            Save();


            Mail mail = new Mail();
            string body = "Se ha reportado una recomendacion.\n";
            body += bodyEmailReporte(reporte);
            mail.enviarMail(mail.MailFrom, "WebRamosUC", "Reporte Numero " + reporte.IDReporte, body);

            return true;
        }
        /// <summary>
        /// revisa si el usuario ya reporto
        /// </summary>
        public bool UserHasReported(Seccion seccion, string userName)
        {
            var usuario = db.Usuarios.SingleOrDefault(d => d.Nombre == userName);
            if (usuario == null)
                return false;
            int id_usuario = usuario.IDUsuario;

            // asi revisa por cada recomendacion de la seccion si el usuario hizo reporte en alguna de ellas.
            foreach( Recomendacion reco in seccion.Recomendaciones)
            {   
            // deben ser todos los reportes de ese usuario no revisados, si estan revisados entonces todo ok( el admin debe ver si borrarla recomendacion o dejar el reporte revisado
            IQueryable<Reporte> reportes = from repo in db.Reportes
                                           where repo.Revisado == 0 && repo.IDUsuario == id_usuario && repo.IDRecomendacion == reco.IDRecomendacion
                                           select repo;
            if (reportes.Count() > 0)
                return true;
            }
            
                return false;
        }

        /// <summary>
        /// Genera un Body para un mail de un reporte
        /// </summary>
        /// <param name="reporte"></param>
        /// <returns></returns>
        public static string bodyEmailReporte(Reporte reporte)
        {
            string body = "Numero de reporte: " + reporte.IDReporte + "\n";
            body += "Curso: " + reporte.Recomendacion.Seccion.ToString2() + "\n";
            foreach (Profesor p in reporte.Recomendacion.Seccion.GetProfesores())
                body += "Profesor: " + p.NombreProfesor + "\n";
            body += "Recomendación hecha por: " + reporte.Recomendacion.Usuario.Nombre + " (" + reporte.Recomendacion.Usuario.Correo + ")\n";
            body += "Reportado por " + reporte.Usuario.Nombre + " (" + reporte.Usuario.Correo + ")";

            return body;
        }

        /// <summary>
        /// Si no se está revisando los reportes sin revision, empieza la revision
        /// </summary>
        public static void empezarRevisionReportesAntiguos()
        {
            if (!revisandoReportes)
            {
                revisandoReportes = true;

                clock = new Timer();
                int dias = 7;
                int milisegundos = 1000 * 60 * 60 * 24 * dias;

                clock.Interval = milisegundos;
                clock.Elapsed += new ElapsedEventHandler(RevisarReportesAntiguos);
                clock.Enabled = revisandoReportes;
                    
            }
        }

        /// <summary>
        /// Metodo que itera mientras revisandoReportes sea true, envia mails de reportes sin revisar cada ciertos dias
        /// </summary>
        private static void RevisarReportesAntiguos(object sender, EventArgs e)
        {
            WebRamosUCDataContext database = new WebRamosUCDataContext();

            while (revisandoReportes)
            {
                IQueryable<Reporte> reportes = from reporte in database.Reportes
                           where reporte.Revisado == 0
                           orderby reporte.Fecha ascending
                           select reporte;

                string body = "Los siguientes reportes se han hecho hace más de una semana y no se han revisado:\n\n\n";

                foreach (Reporte rep in reportes)
                {
                    if(rep.Fecha.AddDays(7) < DateTime.Today)
                        body += bodyEmailReporte(rep) + "\n\n\n";
                }

                Mail mail = new Mail();
                mail.enviarMail(mail.MailFrom, "WebRamosUC", "Reportes sin revisar", body);
            }

            clock.Enabled = false;
        }

        /// <summary>
        /// Guarda los cambios.
        /// </summary>
        public void Save()
        {
            db.SubmitChanges();
        }

    }
}